package controller

import (
	"context"
	"develop_product/contract/product"
	"develop_product/contract/user"
	"develop_product/model"
	"develop_product/proto/products"
	"develop_product/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/go-resty/resty/v2"
	"github.com/spf13/viper"
	"go.mongodb.org/mongo-driver/bson"
	"log"
	"math/big"
	"strconv"
	"strings"
)

type Products struct{}

//0x1e98911315d9d18a0d527a1ef0806d7b0c5db42c

func (p *Products) GetProductsList(ctx context.Context, in *products.GetProductsListRequest, out *products.GetProductsListResponse) error {

	if in.Num == 0 || in.Num%9 == 0 { //如果数据返回为0或者对9求余为0则可能有数据

		var product []*model.Product

		result := utils.Db.Limit(9).Offset(in.Num).Find(&product)

		if result.Error != nil { //查询出错
			out.Code = 500
			out.Msg = "商品查询失败"
			return result.Error
		}

		var product_reps []*products.Products
		for i := 0; i < len(product); i++ {
			var product_rep products.Products
			product_rep.Id = int32(product[i].Id)
			product_rep.Name = product[i].ProductName
			product_rep.Price = strconv.FormatFloat(product[i].ProductPrice, 'f', 2, 64)
			product_rep.Pic = product[i].ProductPic
			product_reps = append(product_reps, &product_rep)
		}

		out.Code = 200
		out.Msg = "商品加载成功"
		out.Products = product_reps
		return nil

	}
	out.Code = 201
	out.Msg = "已经展示所有商品"
	return nil
}

func (p *Products) GetProductDetail(ctx context.Context, in *products.GetProductDetailRequest, out *products.GetProductDetailResponse) error {
	//根据id去数据库查询数据
	var product model.Product
	result := utils.Db.First(&product, "id = ?", in.Id)
	if result.Error != nil { //说明数据查询失败
		return result.Error //返回错误
	}
	//返回商品数据
	out.ProductName = product.ProductName
	out.ProductDesc = product.ProductDesc
	out.ProductPic = product.ProductPic
	out.ProductPrice = float32(product.ProductPrice)
	out.ProductAddress = product.ProductAddress
	out.ProductNum = int32(product.ProductNum)
	return nil
}

func (p *Products) GetProductGoodsCar(ctx context.Context, in *products.GetProductGoodsCarRequest, out *products.GetProductGoodsCarResponse) error {
	// 选择 数据库 以及 连接名称
	client := utils.MongoDb.Database("BlockChainDevelop").Collection("BlockChainDevelop")

	//查询数据 根据用户账号来查询
	result := bson.D{{"username", in.UserName}}

	var order model.Order

	err := client.FindOne(context.TODO(), result).Decode(&order)
	if err != nil {
		out.Code = 500
		out.Msg = "获取购物车信息失败"
		return err
	}

	var products_rep []*products.Order
	for i := 0; i < len(order.Goods); i++ {
		var product_rep products.Order
		product_rep.Id = order.Goods[i].Id
		product_rep.ProductName = order.Goods[i].ProductName
		product_rep.ProductPic = order.Goods[i].ProductPic
		product_rep.ProductPrice = order.Goods[i].ProductPrice
		product_rep.OrderNum = order.Goods[i].OrderNum
		product_rep.OrderAddress = order.Goods[i].OrderAddress
		product_rep.AllOrderNum = order.Goods[i].AllOrderNum
		products_rep = append(products_rep, &product_rep)
	}
	out.Code = 200
	out.Msg = "获取购物车信息成功"
	out.OrderInfo = products_rep
	return nil
}

func (p *Products) AddProductGoodsCar(ctx context.Context, in *products.AddProductGoodsCarRequest, out *products.AddProductGoodsCarResponse) error {
	//使用非关系数据库来储存 购物车内容
	client := utils.MongoDb.Database("BlockChainDevelop").Collection("BlockChainDevelop")

	//根据username 查询
	info := bson.D{{"username", in.Username}}

	var product model.Order //数据储存

	client.FindOne(context.TODO(), info).Decode(&product)

	for i := 0; i < len(product.Goods); i++ {
		if product.Goods[i].Id == in.GoodsId { //商品在购物中
			out.Code = 201
			out.Msg = "宝贝已经在购物车中"
			return nil
		}
	}

	if len(product.Goods) == 0 && len(product.UserName) == 0 { //购物车没有商品需要增加
		product.UserName = in.Username
		var product_rep products.Order
		product_rep.Id = in.GoodsId
		product_rep.OrderAddress = in.Address
		product_rep.OrderNum = in.GoodsNum
		var products_rep model.Product
		utils.Db.First(&products_rep, "id = ?", in.GoodsId)
		product_rep.ProductPrice = float32(products_rep.ProductPrice)
		product_rep.ProductPic = products_rep.ProductPic
		product_rep.ProductName = products_rep.ProductName
		product.Goods = append(product.Goods, product_rep)

		_, err := client.InsertOne(context.TODO(), product)
		if err != nil {
			out.Code = 500
			out.Msg = "加入购物车失败"
			return err
		}
	} else {
		var product_rep products.Order
		product_rep.Id = in.GoodsId
		product_rep.OrderAddress = in.Address
		product_rep.OrderNum = in.GoodsNum
		var products_rep model.Product
		utils.Db.First(&products_rep, "id = ?", in.GoodsId)
		product_rep.ProductPrice = float32(products_rep.ProductPrice)
		product_rep.ProductPic = products_rep.ProductPic
		product_rep.ProductName = products_rep.ProductName
		product.Goods = append(product.Goods, product_rep)

		infos := bson.D{{"$set", bson.D{{"goods", product.Goods}}}}

		_, err := client.UpdateOne(context.TODO(), info, infos)
		if err != nil {
			out.Code = 500
			out.Msg = "加入购物车失败"
			return err
		}
	}
	out.Code = 200
	out.Msg = "加入购物车成功"
	return nil
}

func (p *Products) DeleteGoodsCar(ctx context.Context, in *products.DeleteGoodsCarRequest, out *products.AddProductGoodsCarResponse) error {
	client := utils.MongoDb.Database("BlockChainDevelop").Collection("BlockChainDevelop")

	//现将该用户所有的购物车信息查询出来
	find := bson.D{{"username", in.Username}}

	var order model.Order

	client.FindOne(context.TODO(), find).Decode(&order)

	var delete_back []products.Order
	for i := 0; i < len(order.Goods); i++ { //根据循环条件便利
		if order.Goods[i].Id == in.Id {
			continue
		} else {
			delete_back = append(delete_back, order.Goods[i])
		}
	}

	//实现数据更新
	infos := bson.D{{"$set", bson.D{{"goods", delete_back}}}}

	_, err := client.UpdateOne(context.TODO(), find, infos)
	if err != nil {
		out.Code = 500
		out.Msg = "购物车商品删除失败"
		return err
	}

	out.Code = 200
	out.Msg = "购物车商品删除成功"
	return nil
}

func (p *Products) BuyGoods(ctx context.Context, in *products.AddProductGoodsCarRequest, out *products.AddProductGoodsCarResponse) error {
	//实现商品购买 对象实例化对象
	newProduct, err := product.NewProduct(common.HexToAddress("0x153ab45d8d1d649c2e63c745d446ac1cc649ba31"), utils.Instance)
	if err != nil {
		out.Code = 500
		out.Msg = "商品购买失败"
		return err
	}
	var products model.Product
	result := utils.Db.First(&products, "id = ?", in.GoodsId)
	if result.Error != nil {
		out.Code = 500
		out.Msg = "商品购买失败"
		return result.Error
	}

	formatInt := strconv.FormatInt(int64(in.GoodsNum), 10)
	float, _ := strconv.ParseFloat(formatInt, 2)
	big.NewInt(int64(in.GoodsNum))
	var product_sol product.Order
	product_sol.User = in.Username                        //购买人
	product_sol.Address = in.Address                      //购买地址
	product_sol.OrderNum = big.NewInt(int64(in.GoodsNum)) //购买数量
	product_sol.OrderPrice = strconv.FormatFloat(products.ProductPrice*float, 'f', 2, 64)
	product_sol.OrderName = products.ProductName //商品姓名

	newUser, err := user.NewUser(common.HexToAddress("0x37adf14f4ebbd2eb6254c8bd04eed49731b2fe55"), utils.Instance)
	if err != nil {
		out.Code = 500
		out.Msg = "商品购买失败"
		return err
	}
	balance, err := newUser.BalanceOf(utils.Instance.GetCallOpts(), in.Username)
	if err != nil {
		out.Code = 500
		out.Msg = "商品购买失败"
		return err
	}
	int_balance := utils.StrToInt(balance.String())

	monney := in.GoodsNum * int32(products.ProductPrice)

	cx := big.NewInt(int64(in.GoodsNum) * int64(products.ProductPrice))
	//需要判断用户的余额是否足够
	if int_balance > monney { //证明用户余额充足
		_, _, err = newUser.Transfer(utils.Instance.GetTransactOpts(), in.Username, cx)
		if err != nil {
			out.Code = 500
			out.Msg = "商品购买失败"
			return err
		}
		//订单形成之后 需要减少商品数量
		products.ProductNum = products.ProductNum - int(in.GoodsNum)

		//实现数据库更新
		utils.Db.Model(&products).Update("product_num", products.ProductNum)

		_, receipt, err := newProduct.AddOrder(utils.Instance.GetTransactOpts(), product_sol)
		//从这里获取区块hash
		//设置hash
		_, _, err = newProduct.SetHash(utils.Instance.GetTransactOpts(), receipt.BlockHash)
		if err != nil {
			out.Code = 500
			out.Msg = "商品购买失败"
			return result.Error
		}

		out.Code = 200
		out.Msg = "商品购买成功"
	} else {
		out.Code = 500
		out.Msg = "用户余额不足"
		return nil
	}
	return nil
}

func (p *Products) BuyByGoodsCar(ctx context.Context, in *products.BuyByGoodsCarRequest, out *products.AddProductGoodsCarResponse) error {
	//购物车购买 第一步判断用户余额 是否 达到商品总价
	caller, err := user.NewUser(common.HexToAddress("0x37adf14f4ebbd2eb6254c8bd04eed49731b2fe55"), utils.Instance)
	if err != nil {
		out.Code = 500
		out.Msg = "购买失败，请重试"
		return err
	}
	of, err := caller.BalanceOf(utils.Instance.GetCallOpts(), in.Email)
	if err != nil {
		out.Code = 500
		out.Msg = "购买失败，请重试"
		return err
	}

	balance := of.String()

	if utils.StrToInt(balance) < in.Account { //用户余额不足
		out.Code = 501
		out.Msg = "用户余额不足"
		return nil
	} else { //用户余额是足够的
		client := utils.MongoDb.Database("BlockChainDevelop").Collection("BlockChainDevelop")

		var goods model.Order

		filter := bson.D{{"username", in.Email}}

		//根据用户的购物车内容
		client.FindOne(context.TODO(), filter).Decode(&goods)

		//通过前端返回的索引来购买商品
		for i := 0; i < len(in.Goods); i++ { //进行商品购买

			var product model.Product

			utils.Db.First(&product, "id = ?", goods.Goods[in.Goods[i]].Id)
			//首先判断商品库存是否足够

			if int(goods.Goods[i].OrderNum) < product.ProductNum {
				continue
			} else {
				out.Code = 500
				out.Msg = "选择商品库存不足"
				return nil
			}
		}

		account := big.NewInt(int64(in.Account))

		//经过前面的判断 现在商品可以购买
		_, _, err = caller.Transfer(utils.Instance.GetTransactOpts(), in.Email, account)
		if err != nil {
			out.Code = 500
			out.Msg = "商品购买失败"
			return err
		}

		newProduct, err := product.NewProduct(common.HexToAddress("0x153ab45d8d1d649c2e63c745d446ac1cc649ba31"), utils.Instance)
		if err != nil {
			out.Code = 500
			out.Msg = "商品购买失败"
			return err
		}

		//生成订单
		for i := 0; i < len(in.Goods); i++ { //生成订单的数量
			var order model.Product

			utils.Db.First(&order, "id = ?", goods.Goods[in.Goods[i]].Id)
			//更新商品数量

			old_num := order.ProductNum - int(goods.Goods[in.Goods[i]].OrderNum)
			utils.Db.Model(&order).Update("product_num", old_num)

			var order_info product.Order
			order_info.OrderName = order.ProductName
			num := big.NewInt(int64(goods.Goods[in.Goods[i]].OrderNum * int32(goods.Goods[in.Goods[i]].ProductPrice)))
			order_info.OrderPrice = num.String()
			order_info.OrderNum = big.NewInt(int64(goods.Goods[in.Goods[i]].OrderNum))
			order_info.Address = goods.Goods[in.Goods[i]].OrderAddress
			order_info.User = in.Email

			goods.Goods[in.Goods[i]].ProductName = "ppp"
			//生成订单
			_, receipt, err := newProduct.AddOrder(utils.Instance.GetTransactOpts(), order_info)
			//获取区块hash
			if err != nil {
				out.Code = 500
				out.Msg = "商品购买失败"
				return err
			}
			_, _, err = newProduct.SetHash(utils.Instance.GetTransactOpts(), receipt.BlockHash)
			if err != nil {
				out.Code = 500
				out.Msg = "商品购买失败"
				return err
			}
		}

		//生成订单后将商品删除购物车
		var delete_back []products.Order
		for i := 0; i < len(goods.Goods); i++ { //便利删除用户
			if goods.Goods[i].ProductName == "ppp" {
				continue
			} else {
				delete_back = append(delete_back, goods.Goods[i])
			}
		}
		//实现更新
		filters := bson.D{{"$set", bson.D{{"goods", delete_back}}}}

		client.UpdateOne(context.TODO(), filter, filters) //实现购物车删除已经购买的商品

	}
	out.Code = 200
	out.Msg = "商品购买成功"
	return nil
}

func (p *Products) GetUserOrder(ctx context.Context, in *products.GetUserOrderRequest, out *products.GetUserOrderResponse) error {
	newProduct, err := product.NewProduct(common.HexToAddress("0x153ab45d8d1d649c2e63c745d446ac1cc649ba31"), utils.Instance)
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	_, _, err = newProduct.FindByUser(utils.Instance.GetTransactOpts(), in.Email)
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	returnUser, err := newProduct.ReturnUser(utils.Instance.GetCallOpts())
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	var rep []string //储存用户购买订单的商品名称
	for i := 0; i < len(returnUser); i++ {
		rep = append(rep, returnUser[i].OrderName)
	}

	out.Code = 200
	out.Msg = "获取用户所有订单成功"
	out.Order = rep
	return nil
}

func (p *Products) SearchProduct(ctx context.Context, in *products.SearchProductRequestIndex, out *products.GetProductsListResponse) error {
	search := "%" + in.Search + "%"

	var product []model.Product //储存搜索到的数据

	result := utils.Db.Debug().Find(&product, "product_name like ?", search)
	if result.Error != nil { //查询报错
		out.Code = 500
		out.Msg = "搜索数据错误"
		return result.Error
	}

	var product_reps []*products.Products
	for i := 0; i < len(product); i++ {
		var product_rep products.Products
		product_rep.Id = int32(product[i].Id)
		product_rep.Name = product[i].ProductName
		product_rep.Price = strconv.FormatFloat(product[i].ProductPrice, 'f', 2, 64)
		product_rep.Pic = product[i].ProductPic
		product_reps = append(product_reps, &product_rep)
	}

	out.Code = 200
	out.Msg = "搜索数据成功"
	out.Products = product_reps
	return nil
}

func (p *Products) GetOrderList(ctx context.Context, in *products.GetUserOrderRequest, out *products.GetProductGoodsCarResponse) error {
	newProduct, err := product.NewProduct(common.HexToAddress("0x153ab45d8d1d649c2e63c745d446ac1cc649ba31"), utils.Instance)
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	_, _, err = newProduct.FindByUser(utils.Instance.GetTransactOpts(), in.Email)
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	returnUser, err := newProduct.ReturnUser(utils.Instance.GetCallOpts())
	if err != nil {
		out.Code = 500
		out.Msg = "获取用户所有订单失败"
		return err
	}

	var rep []*products.Order //储存用户购买订单的商品名称
	for i := 0; i < len(returnUser); i++ {
		var keep products.Order
		keep.ProductName = returnUser[i].OrderName
		var product model.Product
		//根据商品名称查询商品信息
		utils.Db.Find(&product, "product_name = ?", returnUser[i].OrderName)
		keep.ProductPrice = utils.StrToFloat32(returnUser[i].OrderPrice)
		keep.OrderAddress = returnUser[i].Address
		keep.OrderNum = utils.StrToInt(returnUser[i].OrderNum.String())
		keep.ProductPic = product.ProductPic
		keep.Hash = returnUser[i].Hash
		keep.Id = int32(product.Id)
		rep = append(rep, &keep)

	}
	out.Code = 200
	out.Msg = "查询用户订单成功"
	out.OrderInfo = rep
	return nil
}

func (p *Products) VerifyHash(ctx context.Context, in *products.VerifyHashRequest, out *products.VerifyHashResponse) error {
	//对url进行读取
	viper.SetConfigFile("./config/database.json")
	viper.SetConfigName("./database")
	viper.SetConfigType("json")
	viper.AddConfigPath("./config")
	viper.ReadInConfig()

	//这里去获取交易hash
	url := "http://" + viper.GetString("url") + ":5100/api/fisco-bcos-browser/block/blockByHash/1/" + in.BlockHash

	client := resty.New()
	//第一个爬取的数据
	rep, err := client.R().Get(url)
	if err != nil {
		log.Println("爬取数据失败：", err.Error())
	}

	//对响应数据进行两次分割
	data := utils.GetBetweenStr(rep.String(), "chainId", "transactionsRoot")

	tses := utils.GetBetweenStr(data, "\"hash\":", ",\"input\":")

	pll := strings.Split(tses, ":\"")

	URL := string([]byte(pll[1]))[:66]

	//http://192.168.40.168:5100/#/transaction/transactionDetail?pkHash=0xf00940117c73d93a9072a3cd5086e2196ce677f69fb798f2b6f34740ace991e5

	transaction := "http://" + viper.GetString("url") + ":5100/#/transaction/transactionDetail?pkHash=" + URL

	out.Msg = "获取区块信息成功"
	out.Code = 200
	out.TransactionsHash=transaction
	return nil
}
